/*
 * Copyright 2008 Stuart Moffatt stuartmoffatt@gmail.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.googlecode.emcode.rvha.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.maps.client.InfoWindowContent;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LocationCallback;
import com.google.gwt.maps.client.geocode.Placemark;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;

public class VulnerabilityInfoWindowHandler {

    private Geocoder geocoder;
    private Placemark placeMark;
    private boolean geoCoded = false;
    public Zillow zillow;
    private HashMap hazards = new HashMap();
    private InfoWindowContent content;
    private LatLng currentCoords;
    
    public VulnerabilityInfoWindowHandler()
    {
        geocoder = new Geocoder();
    }
    
    public void geoCodeAddress(String address)
    {
        
        // geo code the address
        geocoder.getLocations(address, new LocationCallback(){
            

            public void onFailure(int statusCode) {
                // geoCoded is already false
                GWT.log("Failed to geocode address", null);
                Window.alert("Failed to geocode this address");
            }

            public void onSuccess(Placemark[] locations) {
                
                geoCoded = true;
                placeMark = locations[0];
                
                GWT.log("Geocode successful, placemark=" + placeMark.toString(), null);
                
                getZillowInformation();
                
            }
        });
    }
    
    
    public void getHazardInformation(LatLng p)
    {
        
        if( placeMark != null 
                && placeMark.getPoint() != null)
        {
            p = placeMark.getPoint();
        }
        
        // keep track of current coords
        currentCoords = p;
        
        // use the point from the placeMark
        String lat = Double.toString( p.getLatitude() );
        String lng = Double.toString( p.getLongitude() );
        
        String parms = "?lat=" + lat + "&lng=" + lng;
      
        RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, "within.multihazard.php" + parms);
        try
            {
            GWT.log("multihazard request=" + requestBuilder.getUrl(), null);  
            requestBuilder.setCallback(new RequestCallback(){

                public void onError(Request request, Throwable exception) {
                    // TODO handle no hazard information
                    
                }

                public void onResponseReceived(Request request, Response response) {
                    // expecting...
                    // {"multihazard":{"hazards":[{"liquefaction":"<potential>"},{"damfailure":"<Y|N>"}]}}
                    GWT.log("multihazard response=" + response.getText(), null);
                    try
                    {
                        JSONValue jsonValue = JSONParser.parse(response.getText());
                        if( jsonValue.isObject() != null )
                        {
                            JSONObject o = jsonValue.isObject();
                            GWT.log(o.toString(), null);
                            
                            JSONValue multiHazardsNode = o.get("multihazard-information");
                            GWT.log("multihazard-information=" + multiHazardsNode.toString(), null);
                            GWT.log("is object?=" + multiHazardsNode.isObject(), null);
                            
                            JSONArray jsonHazards = (JSONArray) multiHazardsNode.isObject().get("hazards");
                            for (int i = 0; i < jsonHazards.size(); ++i)
                            {
                                // {"type":"liquefaction", "description":"Very Low"}
                                GWT.log(jsonHazards.get(i).toString(), null);
                                
                                String type = jsonHazards.get(i).isObject().get("type").isString().stringValue();
                                String description = jsonHazards.get(i).isObject().get("description").isString().stringValue();
                                
                                Hazard hazard = new Hazard();
                                hazard.setType(type);
                                hazard.setDescription(description);
                                
                                // put in the hash
                                hazards.put(hazard.getType(), hazard);
                                
                                // 
                                
                            }
                            
                            
                        }
                        else
                        {
                           // TODO: say something about not being able to retrieve valid JSON data
                           GWT.log("I think there was an error in JSON parsing", null);
                        }
                        
                        // now build the info window content from Zillow and the hazards array
                        createInfoWindowContent();
                        
                        
                    }
                    catch (JSONException e)
                    {
                        // TODO: Handle JSON exception
                        GWT.log("Major JSON exception:" + e.toString(), null);
                    }
                }
                  
              });
              requestBuilder.send();
          } catch (RequestException e) {
              e.printStackTrace();
          }

        
    }
    
    protected void createInfoWindowContent() {
       
        String text = "";
        
        // 1. If there is a Zillow, use it
        String zillowText = "";
        try
        {
            if( zillow != null )
            {
                String value = zillow.getZestimate().getValuationRange().getHigh().getContent();
                if( value == null )
                {
                    value = "Not available";
                }
                
                String taxAssessmentYear = "";
                if( zillow.getTaxAssessmentYear() != null)
                {
                    taxAssessmentYear = "("+zillow.getTaxAssessmentYear()+")";
                }
                zillowText = "<b>"+zillow.getAddress().toString()+"</b><br/>"
                    + "<b>Value: $"+ value +"</b> "+taxAssessmentYear+"<br/>"
                    + "Built in " + zillow.getYearBuilt() +", "+ zillow.getBedrooms() + " bed, "+zillow.getBathrooms()+" bath<br/>"
                    + zillow.getFinishedSqFt() + " finished sq. ft. on "+zillow.getLotSize()+" sq. ft. lot<br/>"
                    + "<a target=\"_blank\" href=\"http://www.zillow.com\"><img src=\"http://www.zillow.com/static/logos/zillowtiny1.gif\" width=\"80\" height=\"15\" alt=\"Real Estate Valuations\"/></a>";
                            
            }
        }
        catch(Exception e)
        {
            GWT.log("zillowText:" + e.getMessage(), null);
        }
        
        // 2. If here are hazards, use them
        String hazardText = "";
        try
        {
         
            if( !hazards.isEmpty() )
            {
                hazardText = "<br>There are hazard(s) at this location:";
                
                // try liquefaction hazard:
                Hazard liquefaction = (Hazard) hazards.get(Hazard.LIQUEFACTION);
                if( liquefaction != null )
                {
                    hazardText += "<br>The liquefaction potential is <b>" + liquefaction.getDescription() + "</b>";
                }
                
                // try dam failure hazard:
                Hazard damfailure = (Hazard) hazards.get(Hazard.DAMFAILURE);
                if( damfailure != null )
                {
                    if( damfailure.getDescription().equalsIgnoreCase("Y"))
                    {
                        hazardText += "<br>A dam failure threat exists";
                    }
                }
            }
        }
        catch(Exception e)
        {
            GWT.log("hazardText:" + e.getMessage(), null);
        }
        text = zillowText + hazardText;
        GWT.log("text for content=" + text, null);
        
        // now draw the marker
        currentCoords = placeMark.getPoint();
        MainLayout.get().updateMarker(currentCoords, text);
        
    }

    public void getZillowInformation()
      {
          // extract the address
          String street = placeMark.getStreet();
          String zip = placeMark.getPostalCode();
          String url = "pipe.php?";
          url += "a=" + street;
          url += "&z=" + zip;
          GWT.log("pipe url="+url, null);
          RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
            try {
                requestBuilder.sendRequest(null, new ZillowHandler());
              } catch (RequestException ex) {
                  GWT.log("request exception=" + ex, null);
              }
      }  
      
      public class ZillowHandler implements RequestCallback
      {

        public void onError(Request request, Throwable exception) {
            GWT.log("exception=" + exception, null);
            Window.alert("Sorry, that address could not be found.");
        }

        public void onResponseReceived(Request request, Response response) {
            GWT.log("response=" + response.getText(), null);
            try
            {
                JSONValue jsonValue = JSONParser.parse(response.getText());
                if( jsonValue.isObject() != null )
                {
                    JSONObject o = jsonValue.isObject();
                    
                    // get 'results' from 'response'
                    JSONValue valueNode = o.get("value");
                    JSONObject valueObject = valueNode.isObject();
                    
                    JSONArray items = (JSONArray) valueObject.get("items");
                    
                    /*
                     * TODO: Need better error handling here. There are codes
                     * sent back by the Zillow API that I should dig for.
                     * The quick and dirty way is to fetch the items.get(0)
                     * and if it is null, then an exception is thrown.
                     */
                    
                    JSONValue itemsNode = items.get(0);
                    JSONObject itemsObject = itemsNode.isObject();
                    
                    JSONValue responseNode = itemsObject.get("response");
                    JSONObject responseObject = responseNode.isObject();
                    
                    JSONValue resultsValue = responseObject.get("results");
                    JSONObject resultsObject = resultsValue.isObject();
                    
                    JSONValue resultValue = resultsObject.get("result");
                    JSONObject resultObject = resultValue.isObject();
                    
                    if( resultObject == null )
                    {
                        onError(request, null);
                    }
                    else
                    {
                        // from here down maps to a Zillow object
                        zillow = createZillow(resultObject);
                        
                        
                    }
                    
                }
                
                // now fetch hazard information
                /*
                 * Back off on this call because this version of RVHA is not 
                 * backed by PostGIS
                 * getHazardInformation(null); 
                 */
                createInfoWindowContent();
                                
                
            }
            catch (Exception e)
            {
                //TODO handle exception
                GWT.log("Exception=" + e.getMessage(), null);
                Window.alert("There was an error finding this address.");
                MainLayout.get().clearMarker();
            }
            

        }
          
      }
      
      public Zillow createZillow(JSONObject zillowJSONObject)
      {
          // reach into response | results | result
          GWT.log("jsonobject" + zillowJSONObject.toString(), null);
          
          // transfer data from zillowJSONObject to zillow object
          
          // main zillow object
          //TODO: null checks on all of these!!
          Zillow zillow = new Zillow();
          zillow.setZpid( zillowJSONObject.get("zpid").isString().stringValue() );
          zillow.setBathrooms( zillowJSONObject.get("bathrooms").isString().stringValue() );
          zillow.setLotSize( zillowJSONObject.get("lotSizeSqFt").isString().stringValue() );
          //zillow.setLastSoldDate( zillowJSONObject.get("lastSoldDate").isString().stringValue() );
          //zillow.setTaxAssessment( zillowJSONObject.get("taxAssessment").isString().stringValue() );
          //zillow.setTaxAssessmentYear( zillowJSONObject.get("taxAssessmentYear").isString().stringValue() );
          zillow.setYearBuilt( zillowJSONObject.get("yearBuilt").isString().stringValue() );
          zillow.setFinishedSqFt( zillowJSONObject.get("finishedSqFt").isString().stringValue() );
          zillow.setUseCode( zillowJSONObject.get("useCode").isString().stringValue() );
          zillow.setBedrooms( zillowJSONObject.get("bedrooms").isString().stringValue() );

          // zestimate
          JSONValue zestimateValue = zillowJSONObject.get("zestimate");
          JSONObject zestimateObject = zestimateValue.isObject();
          
          // zestimate amount
          JSONValue amountValue = zestimateObject.get("amount");
          JSONObject amountObject = amountValue.isObject();

              ZAmount zAmount = new ZAmount();
              zAmount.setContent( amountObject.get("content").isString().stringValue() );
              zAmount.setCurrency( amountObject.get("currency").isString().stringValue() );
              
              ZValuationRange valuationRange = new ZValuationRange();
              JSONValue valuationRangeValue = zestimateObject.get("valuationRange");
              JSONObject valuationRangeObject = valuationRangeValue.isObject(); 
              
                  ZAmount high = new ZAmount();
                  JSONValue highValue = valuationRangeObject.get("high");
                  JSONObject highObject = highValue.isObject();
                  high.setContent(highObject.get("content").isString().stringValue());
                  high.setCurrency(highObject.get("currency").isString().stringValue());
                  
                  valuationRange.setHigh(high);
                  
                  ZAmount low = new ZAmount();
                  JSONValue lowValue = valuationRangeObject.get("high");
                  JSONObject lowObject = lowValue.isObject();                 
                  low.setContent(lowObject.get("content").isString().stringValue());
                  low.setCurrency(lowObject.get("currency").isString().stringValue());
              
                  valuationRange.setLow(low);
                  
          // zestimate valueChange
          JSONValue valueChangeValue = zestimateObject.get("valueChange");
          JSONObject valueChangeObject = valueChangeValue.isObject();
          
          ZAmount valueChange = new ZAmount();
          valueChange.setContent( valueChangeObject.get("content").isString().stringValue() );
          valueChange.setCurrency( valueChangeObject.get("currency").isString().stringValue() );
        
          // load up zestimate
          ZEstimate zEstimate = new ZEstimate();
          zEstimate.setAmount(zAmount);
          zEstimate.setValuationRange(valuationRange);
          zEstimate.setValueChange(valueChange);
          zEstimate.setLastUpdated(zestimateObject.get("last-updated").isString().stringValue());
          zillow.setZestimate(zEstimate);
          
          // zAddress
          JSONValue addressValue = zillowJSONObject.get("address");
          JSONObject addressObject = addressValue.isObject();
          ZAddress address = new ZAddress();
          address.setCity( addressObject.get("city").isString().stringValue());
          address.setLatitude( addressObject.get("latitude").isString().stringValue());
          address.setLongitude( addressObject.get("longitude").isString().stringValue());
          address.setState(addressObject.get("state").isString().stringValue());
          address.setStreet( addressObject.get("street").isString().stringValue());
          address.setZipCode( addressObject.get("zipcode").isString().stringValue());
          zillow.setAddress(address);
          
          // finally, send it back
          return zillow;
         
      }      

    /**
     * @return the geoCoded
     */
    public boolean isGeoCoded() {
        return geoCoded;
    }

    /**
     * @param geoCoded the geoCoded to set
     */
    public void setGeoCoded(boolean geoCoded) {
        this.geoCoded = geoCoded;
    }

    /**
     * @return the content
     */
    public InfoWindowContent getContent() {
        return content;
    }
    
}
